package com.kaka.rpc.core;

import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;

import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 客户端连接管理器
 *
 * @author zkpursuit
 */
public class NettyCtxManager {

    /**
     * socket通信channel的id，对应一个客户端
     */
    public static final AttributeKey<Object> bind_id = AttributeKey.valueOf("id");

    public static Object getBindId(ChannelHandlerContext ctx) {
        return ctx.channel().attr(bind_id).get();
    }

    public static String getRemoteAddress(ChannelHandlerContext ctx) {
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        return inSocket.getAddress().getHostAddress();
    }

    public static InstanceImpl instance = new InstanceImpl();

    public static <T extends InstanceImpl> T getInstance() {
        return (T) instance;
    }

    public static class InstanceImpl {
        protected final Map<Object, ChannelHandlerContext> clientMap = new ConcurrentHashMap<>();

        /**
         * 移除客户端连接
         *
         * @param clientId 客户端连接唯一标识
         */
        protected void unbind(final Object clientId) {
            ChannelHandlerContext ctx = clientMap.remove(clientId);
            if (ctx == null) return;
            ctx.channel().attr(bind_id).set(null);
        }

        /**
         * 移除客户端连接
         *
         * @param ctx 客户端连接管道
         * @return socket信道绑定的ID
         */
        public Object unbind(ChannelHandlerContext ctx) {
            Object clientId = getBindId(ctx);
            if (clientId != null) {
                ChannelHandlerContext tempCtx = NettyCtxManager.instance.get(clientId);
                if (tempCtx == null) {
                    this.unbind(clientId);
                } else {
                    String tempCtxId = tempCtx.channel().id().asLongText();
                    String ctxId = ctx.channel().id().asLongText();
                    if (ctxId.equals(tempCtxId)) {
                        this.unbind(clientId);
                    }
                }
                return clientId;
            }
            Iterator<Map.Entry<Object, ChannelHandlerContext>> iter = clientMap.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<Object, ChannelHandlerContext> entry = iter.next();
                ChannelHandlerContext channel = entry.getValue();
                if (channel.equals(ctx)) {
                    iter.remove();
                    return ctx.channel().attr(bind_id).getAndSet(null);
                }
            }
            return null;
        }

        /**
         * 添加客户端连接
         *
         * @param ctx      客户端连接管道
         * @param clientId 客户端连接唯一标识
         */
        public void bind(ChannelHandlerContext ctx, Object clientId) {
            Object id = getBindId(ctx);
            if (clientId.equals(id)) return;
            unbind(ctx);
            clientMap.put(clientId, ctx);
            ctx.channel().attr(bind_id).set(clientId);
        }

        /**
         * 获取客户端连接管道
         *
         * @param clientId 客户端连接唯一标识
         * @return 客户端连接管道
         */
        public ChannelHandlerContext get(Object clientId) {
            return clientMap.get(clientId);
        }

        public Map<Object, ChannelHandlerContext> getClientMap() {
            return Collections.unmodifiableMap(clientMap);
        }

        /**
         * 客户端总数量
         */
        public int getClientCount() {
            return clientMap.size();
        }
    }

}